const chalk = require('chalk')
const { default: Decimal } = require('decimal.js')
const {
    green: fnGreen,
    red: fnRed,
    yellow: fnYellow
  } = chalk
const {
    OrderDirection,
    StoreName
} = require('../constants')
const BollService = require('./BollService')
const LogService = require('./LogService')

class KlineService {
    isRed (item = {}) {
        return item.color === 'red'
    }
    isGreen (item = {}) {
        return item.color === 'green'
    }
    logRG (str) {
        let _str = str
            .replace(/\d+/g, v => fnYellow(v))
            .replace(/红/g, fnRed('红'))
            .replace(/绿/g, fnGreen('绿'))
            
        console.log(_str)
    }
    isBigVolumn (klineList, startIndex, direction, bollList) {
        const _klineList = klineList.slice(startIndex)
        const _bollList = bollList.slice(startIndex)
        const item = _klineList[0]
        const bollItem = _bollList[0]
        const bollWidth = bollItem.upper - bollItem.middle
        // const isDirection = (this.isRed(item) && direction === OrderDirection.SELL) || (this.isGreen(item) && direction === OrderDirection.BUY) 
        let maxVolumn = bollWidth * 1.3
        if (maxVolumn < 300) maxVolumn = 300
        if (maxVolumn > 800) maxVolumn = 800
        return item.volumn > maxVolumn
    }
    /**
     * 根据K线数据判断当前是否应该平仓，根据两根反向蜡烛线的原理，或者突然放量
     * @param {Array} klineList K线数组
     * @param {String[buy, sell]} direction 操作方向
     * @param {Array} bollList 布林线
     * @param {Array} minBollWidth 最小布林宽度
     * @returns
     */
    getShouldOneSideStop (klineList, direction, bollList, minBollWidth, isJudgeWidthIncrease) {
        const listIndexAfterLastNarrow = BollService.getIndexAfterLastNarrow(bollList, klineList, minBollWidth, isJudgeWidthIncrease)
        const recentKlineList = klineList.slice(listIndexAfterLastNarrow)
        let shouldOneSideStop = false
        if (!listIndexAfterLastNarrow) {
            shouldOneSideStop = false
        } else {
            let countReverseNum = 0
            if (recentKlineList.length > 3) {
                // 从倒数第二个开始算
                for(let i = recentKlineList.length - 2; i > 0; i--) {
                    const item = recentKlineList[i]
                    if (countReverseNum > 1) break
                    if (!this._isOverSmall(item)) {
                        if (direction === OrderDirection.BUY) {
                            if (this.isRed(item)) {
                                countReverseNum++
                            } else {
                                countReverseNum = 0
                            }
                        }
                        if (direction === OrderDirection.SELL) {
                            if (this.isGreen(item)) {
                                countReverseNum++
                            } else {
                                countReverseNum = 0
                            }
                        }
                    }
                }
            }
            let isCurrentVolumnBig = false, isLastVolumnBig = false
            isCurrentVolumnBig = this.isBigVolumn(recentKlineList, -1, direction, bollList)
            isLastVolumnBig = this.isBigVolumn(recentKlineList, -2, direction, bollList)
            if (
                // 条件1:正向蜡烛交易巨大，该止盈了
                isLastVolumnBig
                || isCurrentVolumnBig
                // 条件2:两根反向蜡烛
                || countReverseNum > 1
            ) shouldOneSideStop = true
        }
        return shouldOneSideStop
    }
    _isOverSmall (kItem) {
        const kSize = Math.abs(kItem.openPrice - kItem.closePrice)
        return Decimal(kSize).div(kItem.openPrice).toNumber() < 0.0004
    }
    /**
     * 根据K线数据判断当前是否应该平仓，根据回到中线原理
     * @param {Array} klineList K线数组
     * @param {String[buy, sell]} direction 操作方向
     * @param {Array} bollList 布林线
     * @param {Array} minBollWidth 最小布林宽度
     * @returns
     */
    getShouldOneSideEnd (direction, bollList, price) {
        const _lastMiddle = bollList[bollList.length - 1].middle
        let isPriceCross = false
        if (
            (direction === OrderDirection.SELL && price > _lastMiddle)
            || (direction === OrderDirection.BUY && price < _lastMiddle)
        ) {
            isPriceCross = true
        }
        return isPriceCross
    }
    /**
     * 根据K线数据判断是否应该开仓，原理是穿出布林带，并连续两根蜡烛同方向
     * @param {Array} klineList K线数组
     * @param {String[buy, sell]} direction 操作方向
     * @param {Array} bollList 布林线
     * @param {Array} minBollWidth 最小布林宽度
     * @returns 
     */
    getShouldOneOpenOrder (price, klineList, direction, bollList, minBollWidth) {
        let shouldOneOpenOrder = false
        const shouldOneSideStop = this.getShouldOneSideStop(klineList, direction, bollList, minBollWidth)
        const {
            upper: bollUp,
            lower: bollDown
        } = bollList[bollList.length - 1]
        const [ _1st, _2st ] = klineList.slice(-3)
        // 该平仓的时候不该开仓
        if (!shouldOneSideStop) {
            switch (direction) {
                case OrderDirection.BUY:
                    if (price > bollUp && this.isGreen(_1st) && this.isGreen(_2st)) {
                        shouldOneOpenOrder = true
                    }
                    break;
                case OrderDirection.SELL:
                    if (price < bollDown && this.isRed(_1st) && this.isRed(_2st)) {
                        shouldOneOpenOrder = true
                    }
                    break;
                default:
                    break;
            }
        }
        return shouldOneOpenOrder
    }
    isKlineReverse (direction, _klineList) {
        const openTime = LogService.getState(StoreName.OPEN_ORDER_TIMESTAMP)
        const klineList = _klineList.filter(item => item.timeStamp >= openTime)
        const [ _1st, _2st, _3st ] = klineList.slice(-4)
        let isKlineReverse = false
        switch (direction) {
            case OrderDirection.SELL:
                if (this.isGreen(_1st) && this.isGreen(_2st) && this.isGreen(_3st)) {
                    isKlineReverse = true
                }
                break;
            case OrderDirection.BUY:
                if (this.isRed(_1st) && this.isRed(_2st) && this.isRed(_3st)) {
                    isKlineReverse = true
                }
                break;
            default:
                break;
        }
        return isKlineReverse
    }
}

module.exports = new KlineService()
